package sg.edu.nus.iss.billing.parse;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.LineNumberReader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
/**
 * @author sammil
 * @param <L>
 *
 */
public abstract class Parser {

	public static final int MAX_LINE_LEN = 100; 
	
	
	/**
	 * Get next line as a StringReader
	 * 
	 * @throws RuntimeException
	 * @throws IOException
	 */
	protected StringReader nextLine(LineNumberReader in) throws IOException,
			RuntimeException {

		StringReader line = new StringReader(in.readLine());
		return line;
	}

	
	/**
	 * Utility method to consume a line and check it begins with the right
	 * character. Assumes mark has already been set, and so calls in.reset() if
	 * check fails.
	 * 
	 * @param in
	 * @param prefix
	 * @return true if line begins with prefix
	 * @throws IOException
	 */
	protected boolean consume(LineNumberReader in, String prefix)
			throws IOException {

		String line = in.readLine();
		if (!line.startsWith(prefix)) {

			System.out.println("Expected '" + prefix + "'; found " + line);
			in.reset();
			return false;
		}
		return true;
	}
	
	/**
	 * Utility method to consume a line and check it begins with the right
	 * character. Assumes mark has already been set, and so calls in.reset() if
	 * check fails.
	 * 
	 * @param in
	 * @param prefix
	 * @return true if line begins with prefix
	 * @throws IOException
	 */
	protected boolean consume(LineNumberReader in, String prefix1,String prefix2)
			throws IOException {

		String line = in.readLine();
		if (!line.startsWith(prefix1) && !line.startsWith(prefix2) ) {

			System.out.println("Expected '" + prefix1 + "' OR '"+ prefix2 +"'; found " + line);
			in.reset();
			return false;
		}
		return true;
	}
	/**
	 * Utility method to consume a blank line Assumes mark has already been set
	 * if check fails
	 * 
	 * @param in
	 * @return true if line is blank
	 * @throws IOException
	 */
	public boolean consumeBlank(LineNumberReader in) throws IOException {

		String line = in.readLine();

		if (line.length() != 0) {

			System.out.println("Expected blank line; found " + line
					+ "at line " + in.getLineNumber());
			in.reset();
			return false;
		}
		return true;
	}

	/**
	 * Utility method to consume whitespace. Assumes mark has already been set.
	 * 
	 * @param in
	 * @return
	 * @throws IOException
	 */
	public boolean consumeWhiteSpaces(LineNumberReader in) throws IOException {

		String line = in.readLine().trim();
		//System.out.println("consumeWhiteSpaces length:" + line.length());
		if (line.length() != 0) {

			System.out.println("Expected white spaces line; found " + line
					+ "at line " + in.getLineNumber());
			in.reset();
			return false;
		}
		return true;
	}


	/**
	 * Tries to read a fixed-length field from the input, as long as the input
	 * starts with the given token. Throws a RuntimeException if it can't read
	 * the right token.
	 * 
	 * @param token
	 * @param input
	 * @param maxLen
	 * @return Field read
	 * @throws IOException
	 */

	protected String readToken(String token, StringReader input, int maxLen)
			throws IOException {

		String possibleToken = readBuf(input, token.length());

		if (!token.equals(possibleToken)) {

			String err = "Expected token '" + token + "'; got '"
					+ possibleToken + "'";
			System.err.println(err);
			throw new RuntimeException(err);
		}
		String result = readBufVar(input, maxLen);
		return result;
	}

	/**
	 * Tries to read a fixed-length field from the input. Throws a
	 * RuntimeException if it can't read enough characters.
	 * 
	 * @param input
	 * @param maxLen
	 * @return Field read
	 * @throws IOException
	 */
	protected String readBuf(StringReader input, int len) throws IOException,
			RuntimeException {

		char[] buf = new char[len];
		int charsRead = input.read(buf, 0, len);
		if (charsRead != len) {

			String error = "Tried to read " + len + " characters; got "
					+ charsRead + ": '" + new String(buf) + "'";
			System.err.println(error);
			throw new RuntimeException(error);
		}
		String result = new String(buf);
		return result;
	}

	/**
	 * Tries to read a variable-length field from the input.
	 * 
	 * @param input
	 * @param maxLen
	 * @return Field read
	 * @throws IOException
	 */
	protected String readBufVar(StringReader input, int len)
			throws IOException, RuntimeException {

		char[] buf = new char[len];
		int charsRead = input.read(buf, 0, len);
		if (charsRead != len) {

			String info = "DEBUG: Tried to read " + len + " characters; got "
					+ charsRead + ": '" + new String(buf) + "'";
			//System.out.println(info);
		}
		String result = new String(buf);
		return result;
	}

	/**
	 * Reads the rest of the line from the input.
	 * 
	 * @param input
	 * @param maxLen
	 * @return Field read
	 * @throws IOException
	 */
	protected String readLine(StringReader input) throws IOException,
			RuntimeException {
		BufferedReader bufReader = new BufferedReader(input);

		String result;

		try {
			result = bufReader.readLine();
		} catch (Exception e) {

			String error = "Tried to read a line got exception";
			System.err.println(error);
			throw new RuntimeException(error);
		}
		return result;
	}
	
	public abstract <L> boolean parse(LineNumberReader in,HashMap<String,ArrayList<L>> outputMap)
	throws IOException;


	

	
}
